package com.gf.framework.mongo.lambda;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.gf.framework.common.model.Page;
import lombok.Data;
import org.bson.BsonRegularExpression;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.lang.Nullable;

import java.util.Collection;
import java.util.regex.Pattern;

/**
 * @author gaofei
 * @date 2022/5/29 15:01
 */
@Data
public class LambdaCriteria {
    private Criteria criteria;
    private Sort sort;
    private int limit = -1;
    private int skip = -1;
    private PageRequest pageRequest;

    public LambdaCriteria(String column) {
        criteria = new Criteria(column);
    }

    public LambdaCriteria(Criteria criteria) {
        this.criteria = criteria;
    }

    public <T> LambdaCriteria(SFunction<T, Object> sFunction) {
        this(MongoLambdaColumn.create().builder(sFunction).getFieldName());
    }

    /**
     * 等于
     * @param value
     * @return
     */
    public LambdaCriteria is(Object value) {
        criteria.is(value);
        return this;
    }

    /**
     * 不等于
     * @param value
     * @return
     */
    public LambdaCriteria ne(Object value) {
        criteria.ne(value);
        return this;
    }

    /**
     * 小于
     * @param value
     * @return
     */
    public LambdaCriteria lt(Object value) {
        criteria.lt(value);
        return this;
    }

    /**
     * 小于等于
     * @param value
     * @return
     */
    public LambdaCriteria lte(Object value) {
        criteria.lte(value);
        return this;
    }

    /**
     * 大于
     * @param value
     * @return
     */
    public LambdaCriteria gt(Object value) {
        criteria.gt(value);
        return this;
    }

    /**
     * 大于等于
     * @param value
     * @return
     */
    public LambdaCriteria gte(Object value) {
        criteria.gte(value);
        return this;
    }

    public LambdaCriteria in(Collection<?> values) {
        criteria.in(values);
        return this;
    }

    public LambdaCriteria in(Object... values) {
        criteria.in(values);
        return this;
    }

    public LambdaCriteria nin(Collection<?> values) {
        criteria.nin(values);
        return this;
    }

    public LambdaCriteria nin(Object... values) {
        criteria.nin(values);
        return this;
    }

    public <T> LambdaCriteria and(SFunction<T, Object> column) {
        return and(MongoLambdaColumn.create().builder(column).getFieldName());
    }

    public LambdaCriteria and(String column) {
        criteria.and(column);
        return this;
    }

    public LambdaCriteria and(Criteria criteria) {
        this.criteria = this.criteria.andOperator(criteria);
        return this;
    }

    public LambdaCriteria and(LambdaCriteria lambdaWrapper) {
        return and(lambdaWrapper.getCriteria());
    }

    public LambdaCriteria or(Criteria criteria) {
        this.criteria = this.criteria.orOperator(criteria);
        return this;
    }

    public LambdaCriteria or(LambdaCriteria lambdaWrapper) {
        return or(lambdaWrapper.getCriteria());
    }

    public LambdaCriteria nor(Criteria criteria) {
        this.criteria = this.criteria.norOperator(criteria);
        return this;
    }
    public LambdaCriteria nor(LambdaCriteria lambdaWrapper) {
        return nor(lambdaWrapper.getCriteria());
    }

    public LambdaCriteria not() {
        criteria.not();
        return this;
    }

    public LambdaCriteria regex(String regex) {
        criteria.regex(regex);
        return this;
    }

    public LambdaCriteria regex(String regex, @Nullable String options) {
        criteria.regex(regex, options);
        return this;
    }

    public LambdaCriteria regex(Pattern pattern) {
        criteria.regex(pattern);
        return this;
    }

    public LambdaCriteria regex(BsonRegularExpression regex) {
        criteria.regex(regex);
        return this;
    }

    public LambdaCriteria all(Object... values) {
        criteria.all(values);
        return this;
    }

    public LambdaCriteria all(Collection<?> values) {
        criteria.all(values);
        return this;
    }

    public LambdaCriteria alike(Example<?> example) {
        criteria.alike(example);
        return this;
    }

    public LambdaCriteria size(int size) {
        criteria.size(size);
        return this;
    }

    public LambdaCriteria exists(boolean value) {
        criteria.exists(value);
        return this;
    }


    public Criteria getCriteria() {
        return criteria;
    }

    public Query getQueryWithNotPage() {
        return new Query(criteria);
    }

    public Query getQuery() {
        Query query = new Query(criteria);
        if (limit > 0) {
            query.limit(limit);
        }
        if (skip > 0) {
            query.skip(0);
        }
        if (sort != null) {
            query.with(sort);
        }
        if (pageRequest != null) {
            query.with(pageRequest);
        }
        return query;
    }

    public LambdaCriteria page(int page, int size) {
        pageRequest = PageRequest.of(page, size);
        return this;
    }

    public LambdaCriteria page(PageRequest pageRequest) {
        this.pageRequest = pageRequest;
        return this;
    }

    /**
     * 升序
     * @param column
     */
    public <T> LambdaCriteria asc(SFunction<T, Object> column) {
        asc(MongoLambdaColumn.create().builder(column).getFieldName());
        return this;
    }

    public LambdaCriteria asc(String column) {
        if (sort == null) {
            sort = Sort.by(Sort.Direction.ASC, column);
        }
        else {
            sort.and(Sort.by(Sort.Direction.ASC, column));
        }
        return this;
    }

    /**
     * 降序
     * @param column
     */
    public <T> LambdaCriteria desc(SFunction<T, Object> column) {
        desc(MongoLambdaColumn.create().builder(column).getFieldName());
        return this;
    }

    public LambdaCriteria desc(String column) {
        if (sort == null) {
            sort = Sort.by(Sort.Direction.DESC, column);
        }
        else {
            sort.and(Sort.by(Sort.Direction.DESC, column));
        }
        return this;
    }

    public LambdaCriteria limit(int limit) {
        this.limit = limit;
        return this;
    }


    public LambdaCriteria skip(int skip) {
        this.skip = skip;
        return this;
    }
}
